package com.tea.zxql.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tea.zxql.domain.Role;
import com.tea.zxql.domain.Sim;
import com.tea.zxql.domain.User;
import com.tea.zxql.domain.dto.*;
import com.tea.zxql.domain.query.UserQuery;
import com.tea.zxql.domain.vo.UserInfoVO;
import com.tea.zxql.mapper.RoleMapper;
import com.tea.zxql.mapper.SimMapper;
import com.tea.zxql.service.UserService;
import com.tea.zxql.mapper.UserMapper;
import com.tea.zxql.utils.MyEncryption;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * @author sdcfj
 * @description 针对表【user】的数据库操作Service实现
 * @createDate 2025-07-17 14:56:50
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private SimMapper simMapper;


    /**
     * 用户登录接口（管理员登录）
     *
     * @param logInDTO 登录信息传输对象，包含用户名、密码和验证码等信息
     * @return 返回登录成功后的token值
     * @throws RuntimeException 验证码错误、用户不存在或密码不正确时抛出异常
     */
    @Override
    public String login(LoginDTO logInDTO) {

        // 从redis中获取验证码
        String redisCode = redisTemplate.opsForValue().get(logInDTO.getUuid());
        if (redisCode != null && !"".equals(redisCode)) {
            redisCode = redisCode.toLowerCase();
        }
        if (logInDTO.getVerifyCode() != null && !"".equals(logInDTO.getVerifyCode())) {
            logInDTO.setVerifyCode(logInDTO.getVerifyCode().toLowerCase());
        }
        // 验证码是否正确
        if (redisCode == null || !redisCode.equals(logInDTO.getVerifyCode()) || logInDTO.getVerifyCode() == null || "".equals(logInDTO.getVerifyCode())) {
            throw new RuntimeException("验证码错误！");
        }

        // 第一步：查询用户
        User user = this.getOne(new QueryWrapper<User>().eq("username", logInDTO.getUsername()));
        if (user == null) {
            throw new RuntimeException("用户信息错误！");
        }

        // 1.1 查看是否角色符合
        List<String> role = userMapper.getRole(user.getUserId());
        if (role == null || role.size() == 0) {
            throw new RuntimeException("用户信息错误！");
        }

        for (int i = 0; i < role.size(); i++) {
            if ("Admin".equals(role.get(i)) || "SuperAdmin".equals(role.get(i))) {
                // 角色符合
                break;
            }
            if (i == role.size() - 1) {
                throw new RuntimeException("用户信息错误！");
            }
        }

        // 第二步：把查询到的用户的密码和前端传来的密码进行比较
        if (!BCrypt.checkpw(logInDTO.getPassword(), user.getPassword())) {
            throw new RuntimeException("用户信息错误！");
        }
        // 第三步：如果密码一致，则返回用户信息
        // 登录到satoken
        StpUtil.login(user.getUserId());
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();

        // 权限获取
        // 通过userMapper这个对象，在数据库中查询当前登录账号的权限集
        List<String> permissions = userMapper.getPermission(user.getUserId());
        // 我们把查询到的结果放入到redis中
        redisTemplate.opsForValue().set(user.getUserId() + ".permissions:", String.valueOf(permissions));

        return tokenInfo.getTokenValue();
    }

    /**
     * 获取用户详细信息
     *
     * @param loginId 当前登录用户的ID
     * @return 用户信息视图对象
     */
    @Override
    public UserInfoVO getUserInfo(Integer loginId) {
        User user = this.getById(loginId);
        log.info("user: {}", user);

        UserInfoVO userInfoVO = new UserInfoVO();

        BeanUtils.copyProperties(user, userInfoVO);

        String role = userMapper.getRoleByUserId(user.getUserId());
        userInfoVO.setRole(role);

        return userInfoVO;
    }

    /**
     * 分页获取用户列表
     *
     * @param query 查询条件封装对象
     * @return 分页结果对象，包含用户信息列表及分页信息
     */
    @Override
    public PageDTO<UserInfoVO> getListPage(UserQuery query) {
        // 创建分页对象
        Page<User> page = new Page<>(query.getPageNo(), query.getPageSize());
        // 创建查询条件对象
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        // 首先判断是超级管理员还是普通管理员
        if (StpUtil.hasRole("SuperAdmin")) {
            if (query.getCompany() != null && !"".equals(query.getCompany())) {
                queryWrapper.eq(User::getCompany, query.getCompany())
                        .eq(User::getIsDeleted, 0);
            } else {
                queryWrapper.eq(User::getIsDeleted, 0);
            }
            Page<User> p = this.page(page, queryWrapper);
            PageDTO<UserInfoVO> pageDTO = new PageDTO<>();
            pageDTO.setTotal((int) p.getTotal());
            pageDTO.setPages((int) p.getPages());
            List<User> users = p.getRecords();

            if (CollUtil.isEmpty(users)) {
                pageDTO.setList(Collections.emptyList());
                return pageDTO;
            }

            List<UserInfoVO> list = BeanUtil.copyToList(users, UserInfoVO.class);
            pageDTO.setList(list);
            return pageDTO;
        } else {
            queryWrapper.eq(User::getCompany, query.getCompany())
                    .eq(User::getIsDeleted, 0);

            Page<User> p = this.page(page, queryWrapper);
            PageDTO<UserInfoVO> pageDTO = new PageDTO<>();
            pageDTO.setTotal((int) p.getTotal());
            pageDTO.setPages((int) p.getPages());
            List<User> users = p.getRecords();

            if (CollUtil.isEmpty(users)) {
                pageDTO.setList(Collections.emptyList());
                return pageDTO;
            }

            List<UserInfoVO> list = BeanUtil.copyToList(users, UserInfoVO.class);
            pageDTO.setList(list);
            return pageDTO;
        }

    }

    /**
     * 添加新用户
     *
     * @param userDTO 用户信息传输对象
     * @throws RuntimeException 用户已存在或角色信息错误时抛出异常
     */
    @Override
    @Transactional
    public void addUser(UserDTO userDTO) {

        Integer loginId = StpUtil.getLoginIdAsInt();

        LambdaQueryWrapper<User> userQueryWrapper = new LambdaQueryWrapper<>();
        userQueryWrapper.eq(User::getUsername, userDTO.getUsername());

        User user = userMapper.selectOne(userQueryWrapper);

        if (user != null) {
            throw new RuntimeException("用户已存在！");
        }

        user = new User();
        BeanUtils.copyProperties(userDTO, user);
        user.setCreationTime(new Date());
        user.setPassword(BCrypt.hashpw(userDTO.getPassword(), BCrypt.gensalt()));

        if ("Admin".equals(userMapper.getRoleByUserId(loginId))) {
            User admin = userMapper.selectById(loginId);
            user.setCompany(admin.getCompany());
        }

        userMapper.insert(user);
        LambdaQueryWrapper<Role> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Role::getRole, userDTO.getRole());
        Role role = roleMapper.selectOne(queryWrapper);
        if (role == null || userDTO.getRole() == null || "".equals(role.getRole()) || "SuperAdmin".equals(userDTO.getRole())) {
            throw new RuntimeException("未知错误！");
        }

        // 如果这个地方是管理员，但是插入的id是管理员的id
        Integer roleId = role.getRoleId();

        if ("Admin".equals(userMapper.getRoleByUserId(loginId))) {
            LambdaQueryWrapper<Role> adminQueryWrapper = new LambdaQueryWrapper<>();
            adminQueryWrapper.eq(Role::getRole, "User");
            Role adminRole = roleMapper.selectOne(adminQueryWrapper);
            // 这个地方就替换为普通用户的id
            roleId = adminRole.getRoleId();
        }

        userMapper.insertUserRole(user.getUserId(), roleId);
    }

    /**
     * 根据ID删除用户
     *
     * @param id 要删除的用户ID
     * @throws RuntimeException 参数错误、不能删除自己或权限不足时抛出异常
     */
    @Override
    public void delById(Integer id) {
        if (id == null) {
            throw new RuntimeException("参数错误！");
        }
        String role = userMapper.getRoleByUserId(StpUtil.getLoginIdAsInt());

        String deleteRole = userMapper.getRoleByUserId(id);

        // 超级管理员是不能被删除的
        if ("SuperAdmin".equals(deleteRole)) {
            throw new RuntimeException("错误操作！");
        }

        // 判断是否删除的是自己
        if (id.equals(StpUtil.getLoginIdAsInt())) {
            throw new RuntimeException("不能删除自己！");
        }

        // 判断当前登录账号的角色
        if ("Admin".equals(role)) {
            User deletUser = this.getById(id);
            if (deletUser == null) {
                throw new RuntimeException("用户不存在！");
            }
            if (!deletUser.getCompany().equals(userMapper.selectById(StpUtil.getLoginIdAsInt()).getCompany())) {
                throw new RuntimeException("错误操作！");
            }
            if ("Admin".equals(deleteRole)) {
                throw new RuntimeException("错误操作！");
            }
            userMapper.deleteById(id);
        } else {
            userMapper.deleteById(id);
        }
    }

    /**
     * 更新用户信息
     *
     * @param userDTO 用户信息传输对象
     * @throws RuntimeException 用户名重复、权限不足或参数错误时抛出异常
     */
    @Override
    @Transactional
    public void updateUser(UserDTO userDTO) {
        // 修改账号的id不能为空
        if (userDTO.getUserId() == null) {
            throw new RuntimeException("错误操作！");
        }
        // 获取当前登录账号的ID，不允许修改自己
        Integer loginId = StpUtil.getLoginIdAsInt();
        if (loginId.equals(userDTO.getUserId())) {
            throw new RuntimeException("错误操作！");
        }
        // 获取当前登录账号的角色
        String loginRole = userMapper.getRoleByUserId(loginId);
        // 查询修改账号的角色信息
        String updateUserRole = userMapper.getRoleByUserId(userDTO.getUserId());
        if ("SuperAdmin".equals(updateUserRole)) {
            throw new RuntimeException("错误操作！");
        }
        // 判断修改的用户名是否重复
        User selectUser = userMapper.selectOne(new LambdaQueryWrapper<User>()
                .ne(User::getUserId, userDTO.getUserId())
                .eq(User::getUsername, userDTO.getUsername()));
        if (selectUser != null) {
            throw new RuntimeException("用户名已存在！");
        }
        // 判断当前登录账号的角色
        // 1. 如果是超级管理员
        if ("SuperAdmin".equals(loginRole)) {
            // 1.1 就可以修改公司的名字
            // 1.1.1 判断公司是否为空
            if (userDTO.getCompany() == null || "".equals(userDTO.getCompany())) {
                throw new RuntimeException("错误操作！");
            }
            User user = new User();
            BeanUtils.copyProperties(userDTO, user);
            user.setPassword(null);
            // 1.2 如果前端传过来的密码不为null或者 ""就要修改密码
            if (userDTO.getPassword() != null && !"".equals(userDTO.getPassword())) {
                user.setPassword(BCrypt.hashpw(userDTO.getPassword(), BCrypt.gensalt()));
            }

            userMapper.updateById(user);

        } else if ("Admin".equals(loginRole)) {
            // 2. 如果是普通管理员
            // 2.1 不能修改公司的名字，并且只能修改自己公司的用户，需要保证整个公司名字和这个普通管理员公司名字一致
            // 2.1.1 查询管理员公司名
            String adminCompany = userMapper.getCompanyByUserId(loginId);
            String updateCompany = userMapper.getCompanyByUserId(userDTO.getUserId());
            if (!adminCompany.equals(updateCompany)) {
                throw new RuntimeException("错误操作！");
            }
            userDTO.setCompany(adminCompany);
            // 2.2 只能修改user角色，不能修改普通管理员和超级管理员
            if (!"User".equals(updateUserRole)) {
                throw new RuntimeException("错误操作！");
            }
            User user = new User();
            BeanUtil.copyProperties(userDTO, user);
            user.setPassword(null);
            // 如果密码不为空，就需要修改密码
            if (userDTO.getPassword() != null && !"".equals(userDTO.getPassword())) {
                user.setPassword(BCrypt.hashpw(userDTO.getPassword(), BCrypt.gensalt()));
            }
            userMapper.updateById(user);
        }

    }

    /**
     * 获取所有公司名称列表
     *
     * @return 公司名称列表
     */
    @Override
    public List<String> getCompanyList() {
        return userMapper.getCompanyList();
    }

    /**
     * 批量添加用户
     *
     * @param addUserListDTO 批量添加用户信息传输对象
     * @throws RuntimeException 数量小于0或公司名称为空时抛出异常
     */
    @Override
    @Transactional
    @Synchronized
    public void addUserList(AddUserListDTO addUserListDTO) {

        // 数量不能小于0
        if (addUserListDTO.getNumber() <= 0 || addUserListDTO.getNumber() == null) {
            throw new RuntimeException("数量不能小于0");
        }
        // 公司名字也不能为空
        if (addUserListDTO.getCompany() == null || addUserListDTO.getCompany().equals("")) {
            throw new RuntimeException("公司名字不能为空");
        }
        Integer num = 0;
        String prefix = "zxql";
        // 首先要获取到当前有多少了？
        if (addUserListDTO.getPrefix() != null && !addUserListDTO.getPrefix().equals("")) {
            prefix = addUserListDTO.getPrefix();
            // 需要查询数据库是否有前缀
            num = userMapper.getUserNum(addUserListDTO.getPrefix());
            if (num == null) {
                num = 0;
                // 插入数据库
                userMapper.insertKeyWord(addUserListDTO.getPrefix());
            }
        } else {
            num = userMapper.getUserNum(prefix);
        }

        Integer userNum = 0;
        // 前端传入需要新增的账号数量，用List来初始化一下
        List<User> userList = new ArrayList<>(addUserListDTO.getNumber());
        for (int i = 0; i < addUserListDTO.getNumber(); i++) {
            User user = new User();
            // 设置密码
            user.setPassword(BCrypt.hashpw(addUserListDTO.getPassword(), BCrypt.gensalt()));
            // 设置公司
            user.setCompany(addUserListDTO.getCompany());
            // 设置用户名
            userNum = num + i;
            String userNumStr = String.valueOf(userNum);
            Integer userNumLen = userNumStr.length();
            for (int j = 0; j < 8 - userNumLen; j++) {
                userNumStr = "0" + userNumStr;
            }
            user.setUsername(prefix + userNumStr);
            // 查询是否存在
            if (!(userMapper.selectOne(new QueryWrapper<User>().eq("username", user.getUsername())) == null)) {
                i--;
                num++;
                continue;
            }
            user.setCreationTime(new Date());
            user.setIsDeleted(0);
            userList.add(user);
        }

        userMapper.insert(userList);
        for (int i = 0; i < userList.size(); i++) {
            userMapper.insertUserRole(userList.get(i).getUserId(), 3);
        }

        userMapper.updateKeyWord(prefix, num + userList.size());

    }

    /**
     * 普通用户登录接口
     *
     * @param logInDTO 登录信息传输对象，包含用户名、密码、验证码和设备SIM卡ID
     * @return 登录成功后的token值
     * @throws RuntimeException 验证码错误、用户不存在、密码错误或设备不存在时抛出异常
     */
    @Override
    @Transactional
    public String userLogin(LoginDTO logInDTO) {

        // 从redis中获取验证码
        String redisCode = redisTemplate.opsForValue().get(logInDTO.getUuid());
        if (redisCode != null && !"".equals(redisCode)) {
            redisCode = redisCode.toLowerCase();
        }
        if (logInDTO.getVerifyCode() != null && !"".equals(logInDTO.getVerifyCode())) {
            logInDTO.setVerifyCode(logInDTO.getVerifyCode().toLowerCase());
        }
        // 验证码是否正确
        if (redisCode == null || !redisCode.equals(logInDTO.getVerifyCode()) || logInDTO.getVerifyCode() == null || "".equals(logInDTO.getVerifyCode())) {
            throw new RuntimeException("验证码错误！");
        }

        // 查询用户
        User user = userMapper.selectOne(new QueryWrapper<User>().eq("username", logInDTO.getUsername()));
        if (user == null) {
            throw new RuntimeException("用户不存在！");
        }

        // 校验密码
        if (!MyEncryption.checkPwd(logInDTO.getPassword(), user.getPassword())) {
            throw new RuntimeException("用户不存在！");
        }

        // 获取用户的角色信息
        String role = userMapper.getRoleByUserId(user.getUserId());
        if (!"User".equals(role)) {
            throw new RuntimeException("用户不存在！");
        }

        // 通过前端传入的simId查询设备是否存在
        Sim sim = simMapper.selectBySimId(logInDTO.getSimId());
        if (sim == null || sim.getStatus() != 0) {
            throw new RuntimeException("设备不存在！");
        }
        sim.setCompany(user.getCompany());

        StpUtil.login(user.getUserId());

        String token = StpUtil.getTokenInfo().getTokenValue();

        LambdaUpdateWrapper<Sim> simUpdateWrapper = new LambdaUpdateWrapper<>();
        simUpdateWrapper.set(Sim::getStatus, 1)
                .set(Sim::getCompany, user.getCompany())
                .eq(Sim::getId, sim.getId())
                .and(wrapper -> wrapper.eq(Sim::getStatus, sim.getStatus()));

        // 权限获取
        // 通过userMapper这个对象，在数据库中查询当前登录账号的权限集
        List<String> permissions = userMapper.getPermission(user.getUserId());
        // 我们把查询到的结果放入到redis中
        redisTemplate.opsForValue().set(user.getUserId() + ".permissions:", String.valueOf(permissions));

        redisTemplate.opsForValue().set(sim.getSimId() + "company", user.getCompany());

        redisTemplate.opsForValue().set(sim.getSimId() + "userId", String.valueOf(user.getUserId()));

        redisTemplate.opsForValue().set(user.getUserId() + "userIdGetsimId:", sim.getSimId());

        // 在这个地方把蜂鸣器和led的状态配置到redis中
        redisTemplate.opsForValue().set(sim.getSimId() + ".buzzer", sim.getBuzzer());
        redisTemplate.opsForValue().set(sim.getSimId() + ".led", sim.getLed());

        int count = simMapper.update(sim, simUpdateWrapper);
        if (count == 0) {
            throw new RuntimeException("设备不存在！");
        }

        return token;
    }

    /**
     * 用户登出接口
     * 清除相关redis缓存并更新设备状态
     */
    @Override
    public void logout() {
        // 获取当前登录的用户id
        int userId = StpUtil.getLoginIdAsInt();

        String simId = redisTemplate.opsForValue().get(userId + "userIdGetsimId:");

        // 清除redis中绑定的数据
        redisTemplate.delete(simId + "company");
        redisTemplate.delete(simId + "userId");
        redisTemplate.delete(userId + "userIdGetsimId:");
        redisTemplate.delete(simId + ".buzzer");
        redisTemplate.delete(simId + ".led");
        redisTemplate.delete(simId + ".old.buzzer");
        redisTemplate.delete(simId + ".old.led");

        simMapper.delete(new LambdaQueryWrapper<Sim>().eq(Sim::getSimId, simId));
        simMapper.update(new LambdaUpdateWrapper<Sim>()
                .set(Sim::getStatus, 0)
                .eq(Sim::getSimId, simId));

        StpUtil.logout();

    }

    /**
     * 获取当前登录用户的角色信息
     *
     * @return 用户信息视图对象，包含角色和设备SIM卡ID
     */
    @Override
    public UserInfoVO getUserRoleInfo() {
        UserInfoVO userInfoVO = new UserInfoVO();

        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserId, StpUtil.getLoginIdAsInt()));

        BeanUtil.copyProperties(user, userInfoVO);
        String simId = redisTemplate.opsForValue().get(user.getUserId() + "userIdGetsimId:");
        userInfoVO.setSimId(simId);

        return userInfoVO;
    }

    /**
     * 更新用户密码
     *
     * @param updatePasswordDTO 密码更新信息传输对象
     * @throws RuntimeException 新密码不一致或旧密码错误时抛出异常
     */
    @Override
    public void updatePassword(UpdatePasswordDTO updatePasswordDTO) {

        // 1. 判断新密码和再次输入的新密码是否一致
        if (!(updatePasswordDTO.getNewPass() != null && updatePasswordDTO.getNewPass().equals(updatePasswordDTO.getAgainPass()))) {
            throw new RuntimeException("新密码有误!");
        }

        // 2. 判断旧密码是否正确
        // 2.1 获取当前登录的id
        Integer loginId = StpUtil.getLoginIdAsInt();
        // 2.2 从数据库中获取到用户对象
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserId, loginId));
        // 2.3 进行判断验证
        if (!(updatePasswordDTO.getOldPass() != null && MyEncryption.checkPwd(updatePasswordDTO.getOldPass(), user.getPassword()))) {
            throw new RuntimeException("旧密码有误!");
        }

        user.setPassword(MyEncryption.encrypt(updatePasswordDTO.getNewPass()));
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<User>();
        updateWrapper.set(User::getPassword, user.getPassword());
        updateWrapper.eq(User::getUserId, user.getUserId());

        // 执行了下面这个代码，数据库才会更新
        userMapper.update(updateWrapper);

    }

}
